using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Project01
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont spriteFont;
        InputListener inputListener;

        private Vector2 screenDimension;
        private Texture2D xCoordinate;
        private Vector2 xCoorPosition;
        private Texture2D yCoordinate;
        private Vector2 yCoorPosition;
        private Vector2 centerCoordinate;
                
        private Car car;
        private GasTank gasTank;
        private Driver driver;

        private string infoString;
        private Vector2 infoPosition;
        private int totalMass;
        private Vector2 centerOfMassPosition;
        private Texture2D centerOfMassTexture;
        private long momentOfInteria;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            this.IsMouseVisible = true;
            //graphics.PreferredBackBufferWidth = 800;
            //graphics.PreferredBackBufferHeight = 800;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            inputListener = new InputListener(this);
            Components.Add(inputListener);

            screenDimension = new Vector2(graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);

            xCoordinate = CreateRectangle((int)screenDimension.Y, 1, Color.White);
            xCoorPosition = new Vector2(0, 240);
            yCoordinate = CreateRectangle(1, (int)screenDimension.Y, Color.White);
            yCoorPosition = new Vector2(240, 0);

            centerCoordinate = new Vector2(240,240);

            car = new Car(new Vector2(200, 100), new Vector2(100, 100), 1000, GraphicsDevice);
            gasTank = new GasTank(new Vector2(40, 80), new Vector2(20, 100), 200, GraphicsDevice);
            driver = new Driver(new Vector2(40, 40), new Vector2(160, 120), 200, GraphicsDevice);

            infoPosition = new Vector2(500, 50);

            centerOfMassTexture = CreateRectangle(3, 3, Color.DarkRed);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            spriteFont = Content.Load<SpriteFont>("gameFont");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (inputListener.isKeyDown(Keys.Escape))
                this.Exit();

            // TODO: Add your update logic here
            gasTank.Update(inputListener);
            gasTank.CarLocation = car.Location;
            driver.Update(inputListener);
            driver.CarLocation = car.Location;
            CarMovement();

            //Calculate total mass
            totalMass = car.Mass + gasTank.Mass + driver.Mass;

            //Calculate center of mass
            centerOfMassPosition.X = (car.Mass * car.Position.X + gasTank.Mass * gasTank.Position.X + driver.Mass * driver.Position.X) / totalMass;
            centerOfMassPosition.Y = (car.Mass * car.Position.Y + gasTank.Mass * gasTank.Position.Y + driver.Mass * driver.Position.Y) / totalMass;

            car.MomentOfInertiaCenter = (car.Mass * (Math.Pow(car.Dimension.X, 2.0) + Math.Pow(car.Dimension.Y, 2.0))) / 12;
            gasTank.MomentOfInertiaCenter = (gasTank.Mass * (Math.Pow(gasTank.Dimension.X, 2.0) + Math.Pow(gasTank.Dimension.Y, 2.0))) / 12;
            driver.MomentOfInertiaCenter = (driver.Mass * (Math.Pow(driver.Dimension.X, 2.0) + Math.Pow(driver.Dimension.Y, 2.0))) / 12;


            //Calculate distance between each object and the center of mass
            car.Distance = Math.Sqrt((Math.Pow((car.Position.X - centerOfMassPosition.X), 2.0) + Math.Pow((car.Position.Y - centerOfMassPosition.Y), 2.0)));
            gasTank.Distance = Math.Sqrt((Math.Pow((gasTank.Position.X - centerOfMassPosition.X), 2.0) + Math.Pow((gasTank.Position.Y - centerOfMassPosition.Y), 2.0)));
            driver.Distance = Math.Sqrt(Math.Pow((driver.Position.X - centerOfMassPosition.X), 2.0) + Math.Pow((driver.Position.Y - centerOfMassPosition.Y), 2.0));            

            //Calculate moment of inertia of each object about the center of mass
            car.MomentOfInertiaCOM = (car.Mass * Math.Pow(car.Distance, 2.0) + car.MomentOfInertiaCenter);
            gasTank.MomentOfInertiaCOM = (gasTank.Mass * Math.Pow(gasTank.Distance, 2.0) + gasTank.MomentOfInertiaCenter);
            driver.MomentOfInertiaCOM = (driver.Mass * Math.Pow(driver.Distance, 2.0) + driver.MomentOfInertiaCenter);

            //Calculate moment of inertia of the composite object
            momentOfInteria = (long)(car.MomentOfInertiaCOM + gasTank.MomentOfInertiaCOM + driver.MomentOfInertiaCOM);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Gray);

            // TODO: Add your drawing code here
            spriteBatch.Begin();

            //Drawing coordinate
            spriteBatch.Draw(xCoordinate, xCoorPosition, Color.White);
            spriteBatch.Draw(yCoordinate, yCoorPosition, Color.White);            
            
            //Drawing objects
            car.Draw(spriteBatch);
            gasTank.Draw(spriteBatch);
            driver.Draw(spriteBatch);

            //Drawing center of mass
            spriteBatch.Draw(centerOfMassTexture, new Vector2(centerCoordinate.X + centerOfMassPosition.X,
                centerCoordinate.Y - centerOfMassPosition.Y), Color.White);

            //Display infomation
            infoString = "Car Position: (" + car.Position.X + "," + car.Position.Y + ")" +
                "\nTank Position: (" + gasTank.Position.X + "," + gasTank.Position.Y + ")" +
                "\nDriver Position: (" + driver.Position.X + "," + driver.Position.Y + ")";

            infoString += "\n\n" + "Total Mass: " + car.Mass + " + " + gasTank.Mass + " + " + driver.Mass + " = "+ totalMass + " kg";
            infoString += "\n\n" + "Center Of Mass: (" + centerOfMassPosition.X + "," + centerOfMassPosition.Y + ")";
            //infoString += "\n\n" + "MOI Center: " + car.MomentOfInertiaCenter +"\n" + gasTank.MomentOfInertiaCenter + "\n" + driver.MomentOfInertiaCenter;
            //infoString += "\n\n" + "Distance: " + car.Distance + "\n" + gasTank.Distance + "\n" + driver.Distance;
            //infoString += "\n\n" + "MOI COM: " + car.MomentOfInertiaCOM + "\n" + gasTank.MomentOfInertiaCOM + "\n" + driver.MomentOfInertiaCOM;
            infoString += "\n\n" + "Moment Of Inertia: " + momentOfInteria + " kg m^2";

            spriteBatch.DrawString(spriteFont, infoString, infoPosition, Color.Black);
            spriteBatch.End();

            base.Draw(gameTime);
        }

        private Texture2D CreateRectangle(int width, int height, Color objColor)
        {
            Texture2D rectTexture = new Texture2D(GraphicsDevice, width, height, false, SurfaceFormat.Color);

            Color[] color = new Color[width * height];

            for (int i = 0; i < color.Length; i++)
                color[i] = objColor;

            rectTexture.SetData(color);

            return rectTexture;
        }

        private void CarMovement()
        {
            if (inputListener.isKeyPressed(Keys.Left))
            {
                car.Position -= new Vector2(10, 0);
                car.Location -= new Vector2(10, 0);
                car.XCoorPosition -= new Vector2(10, 0);
                car.YCoorPosition -= new Vector2(10, 0);
                gasTank.Position -= new Vector2(10, 0);
                gasTank.Location -= new Vector2(10, 0);
                gasTank.XCoorPosition -= new Vector2(10, 0);
                gasTank.YCoorPosition -= new Vector2(10, 0);
                driver.Position -= new Vector2(10, 0);
                driver.Location -= new Vector2(10, 0);
                driver.XCoorPosition -= new Vector2(10, 0);
                driver.YCoorPosition -= new Vector2(10, 0);
            }

            if (inputListener.isKeyPressed(Keys.Right))
            {
                car.Position += new Vector2(10, 0);
                car.Location += new Vector2(10, 0);
                car.XCoorPosition += new Vector2(10, 0);
                car.YCoorPosition += new Vector2(10, 0);
                gasTank.Position += new Vector2(10, 0);
                gasTank.Location += new Vector2(10, 0);
                gasTank.XCoorPosition += new Vector2(10, 0);
                gasTank.YCoorPosition += new Vector2(10, 0);
                driver.Position += new Vector2(10, 0);
                driver.Location += new Vector2(10, 0);
                driver.XCoorPosition += new Vector2(10, 0);
                driver.YCoorPosition += new Vector2(10, 0);
            }

            if (inputListener.isKeyPressed(Keys.Up))
            {

                car.Position += new Vector2(0, 10);
                car.Location -= new Vector2(0, 10);
                car.XCoorPosition -= new Vector2(0, 10);
                car.YCoorPosition -= new Vector2(0, 10);
                gasTank.Position += new Vector2(0, 10);
                gasTank.Location -= new Vector2(0, 10);
                gasTank.XCoorPosition -= new Vector2(0, 10);
                gasTank.YCoorPosition -= new Vector2(0, 10);
                driver.Position += new Vector2(0, 10);
                driver.Location -= new Vector2(0, 10);
                driver.XCoorPosition -= new Vector2(0, 10);
                driver.YCoorPosition -= new Vector2(0, 10);
            }

            if (inputListener.isKeyPressed(Keys.Down))
            {
                car.Position -= new Vector2(0, 10);
                car.Location += new Vector2(0, 10);
                car.XCoorPosition += new Vector2(0, 10);
                car.YCoorPosition += new Vector2(0, 10);
                gasTank.Position -= new Vector2(0, 10);
                gasTank.Location += new Vector2(0, 10);
                gasTank.XCoorPosition += new Vector2(0, 10);
                gasTank.YCoorPosition += new Vector2(0, 10);
                driver.Position -= new Vector2(0, 10);
                driver.Location += new Vector2(0, 10);
                driver.XCoorPosition += new Vector2(0, 10);
                driver.YCoorPosition += new Vector2(0, 10);
            }
        }
    }
}
